import pandas as pd
import numpy as np
from data_resource.data_bases import engine
from utilities.utilities_statics import factor_standardize
from factorAnaly.financialEngine import FinancialFactor

# 获取基础数据：归母净利润、营业收入、毛利率、ROE季度
sql = """
    with iq_quarter as (
        select ticker as code, end_date, f_ann_date, revenue, n_income_attr_p, 
        case
            when total_revenue is null then null
            when total_revenue = 0 then null
            else 1-oper_cost/total_revenue
            end as gross_margin,
        n_income
        from (
            select *, row_number() over (
                partition by ticker, end_date
                order by update_flag desc, f_ann_date
            ) as rn
        from quant_research."financials_incomeState_quarter"
        ) iq
        where iq.rn=1
        order by end_date
    ),
    bs_lt as (
        select bs.ticker, bs.f_ann_date, bs.end_date, bs.total_assets, bs.total_liab, (bs.total_assets - bs.total_liab) as equity,
            -- 计算当期及上期净资产的均值
            ((bs.total_assets - bs.total_liab) + 
             lag(bs.total_assets - bs.total_liab, 1) over (
                 partition by bs.ticker 
                 order by bs.end_date
             )) / 2.0 as avg_equity
        from (
            select *, row_number() over (
                partition by ticker, end_date
                order by update_flag desc, f_ann_date
            ) as rn
            from quant_research.financials_bs_lt
        ) bs
        where bs.rn=1
    )
    -- 取数 
    select a.code, a.end_date, a.f_ann_date, a.revenue, a.n_income_attr_p, a.gross_margin, 
        case
            when b.avg_equity is null then null
            when b.avg_equity = 0 then null
            else a.n_income/b.avg_equity 
            end as roe_q
    from iq_quarter as a
    left join bs_lt as b on a.code=b.ticker and a.end_date=b.end_date
    order by a.end_date;
"""


# 标准算子
def growth(data: pd.Series, period: int = 4):
    """计算同比百分比增速"""
    period = period + 1
    _r = np.full(len(data), np.nan)
    if len(data) < period:
        return pd.Series(_r, index=data.index)

    data_growth = data.rolling(period).apply(lambda x: (x.iloc[-1] - x.iloc[0]) / x.iloc[0])
    return pd.Series(data_growth, index=data.index)


def sue(df: pd.Series):
    """预期外增速"""

    def _core(data):
        try:
            _draft = np.mean(np.diff(data.iloc[0:8]))
            _forecast = data.iloc[7] + _draft

            return data.iloc[8] - _forecast
        except Exception as e:
            print(e)

    _r = np.full(len(df), np.nan)
    if len(df) < 9:
        return pd.Series(_r, index=df.index)
    else:
        # 每9个数字构建一个滑动窗口，当期及过去2年财务数据变化
        signals = df.rolling(window=9).apply(lambda x: _core(x))

    return pd.Series(signals, index=df.index)


def acceleration(data: pd.Series, period: int = 1):
    """百分比增速加速度"""
    return data.diff(period)


def robust_growth(data: pd.Series, period: int = 4):
    """稳健增速=(x_t - x_t-n)/std()"""
    period = period + 1
    _r = np.full(len(data), np.nan)
    if len(data) < period:
        return pd.Series(_r, index=data.index)
    data_robust = data.rolling(period).apply(lambda x: (x.iloc[-1] - x.iloc[0]) / np.std(x))
    return pd.Series(data_robust, index=data.index)


if __name__ == "__main__":
    raw = pd.read_sql(sql, con=engine)
    raw.dropna(inplace=True)
    # 综合增速因子
    raw['revenue_growth_1y'] = raw.groupby('code')['revenue'].transform(growth, period=4)
    raw['revenue_growth_halfY'] = raw.groupby('code')['revenue'].transform(growth, period=2)
    raw['profit_growth_1y'] = raw.groupby('code')['n_income_attr_p'].transform(growth, period=4)
    raw['profit_growth_halfY'] = raw.groupby('code')['n_income_attr_p'].transform(growth, period=2)
    raw['suprise_growth'] = raw.groupby('code')['n_income_attr_p'].transform(sue)
    raw['profit_accele_1y'] = raw.groupby('code')['n_income_attr_p'].transform(acceleration, period=4)
    raw['profit_robust_1y'] = raw.groupby('code')['n_income_attr_p'].transform(robust_growth, period=4)

    raw.dropna(inplace=True)

    # 去极值
    raw['revenue_growth_1y'] = raw.groupby('end_date')['revenue_growth_1y'].transform(
        lambda x: factor_standardize(x, extreme=True, if_standardize=True, extreme_thred=5))
    raw['revenue_growth_halfY'] = raw.groupby('end_date')['revenue_growth_halfY'].transform(
        lambda x: factor_standardize(x, extreme=True, if_standardize=True, extreme_thred=5))
    raw['profit_growth_1y'] = raw.groupby('end_date')['profit_growth_1y'].transform(
        lambda x: factor_standardize(x, extreme=True, if_standardize=True, extreme_thred=5))
    raw['profit_growth_halfY'] = raw.groupby('end_date')['profit_growth_halfY'].transform(
        lambda x: factor_standardize(x, extreme=True, if_standardize=True, extreme_thred=5))
    raw['suprise_growth'] = raw.groupby('end_date')['suprise_growth'].transform(
        lambda x: factor_standardize(x, extreme=True, if_standardize=True, extreme_thred=5))
    raw['profit_accele_1y'] = raw.groupby('end_date')['profit_accele_1y'].transform(
        lambda x: factor_standardize(x, extreme=True, if_standardize=True, extreme_thred=5))
    raw['profit_robust_1y'] = raw.groupby('end_date')['profit_robust_1y'].transform(
        lambda x: factor_standardize(x, extreme=True, if_standardize=True, extreme_thred=5))

    signal_growth = raw[['code', 'end_date', 'f_ann_date',
                         'revenue_growth_1y', 'revenue_growth_halfY',
                         'profit_growth_1y', 'profit_growth_halfY',
                         'suprise_growth', 'profit_accele_1y', 'profit_robust_1y']].copy()

    # 成长因子回测
    signal_growth['signal_growth'] = signal_growth['revenue_growth_1y'] + signal_growth['revenue_growth_halfY'] + \
                                     signal_growth['profit_growth_1y'] + signal_growth['profit_growth_halfY'] + \
                                     signal_growth['suprise_growth'] + signal_growth['profit_accele_1y'] + \
                                     signal_growth['profit_robust_1y']

    print("------- 开始因子回测 ----------")
    f_growth = FinancialFactor(factor=signal_growth)  # 默认：20交易日调仓周期
    f_growth.group_report(signal_name='signal_growth')
    f_growth.plotting_group_return(signal_name='signal_growth')
